
Anh Tuan
Data Science Expert

Đối với tự động hóa web, Browser4 (từ PulsarRPA) đã trở thành động cơ trình duyệt nhanh như chớp, an toàn với coroutine, được thiết kế dành riêng cho trích xuất dữ liệu được hỗ trợ bởi AI. Với khả năng hỗ trợ 100k-200k truy cập trang phức tạp mỗi máy mỗi ngày, Browser4 được xây dựng để mở rộng quy mô. Tuy nhiên, khi trích xuất dữ liệu từ các trang web được bảo vệ, các thách thức CAPTCHA trở thành rào cản quan trọng.
CapSolver cung cấp sự kết hợp hoàn hảo với khả năng tự động hóa của Browser4, cho phép các đại diện của bạn dễ dàng vượt qua các trang web bị bảo vệ bằng CAPTCHA. Sự tích hợp này kết hợp khả năng tự động hóa trình duyệt tốc độ cao của Browser4 với giải pháp CAPTCHA hàng đầu ngành.
Browser4 là khung phần mềm tự động hóa trình duyệt hiệu năng cao được xây dựng bằng Kotlin. Nó được thiết kế dành riêng cho các ứng dụng AI yêu cầu khả năng tự động hóa đại diện, tốc độ xử lý cực cao và trích xuất dữ liệu kết hợp giữa LLM, thuật toán học máy và phương pháp dựa trên bộ chọn.
| Phương thức | Mô tả |
|---|---|
session.open(url) |
Tải trang và trả về một PageSnapshot |
session.parse(page) |
Chuyển đổi snapshot thành tài liệu trong bộ nhớ |
driver.selectFirstTextOrNull(selector) |
Lấy văn bản từ DOM đang chạy |
driver.evaluate(script) |
Thực thi JavaScript trong trình duyệt |
session.extract(document, fieldMap) |
Ánh xạ các bộ chọn CSS thành các trường có cấu trúc |
CapSolver là dịch vụ giải CAPTCHA hàng đầu cung cấp các giải pháp dựa trên AI để vượt qua các thách thức CAPTCHA khác nhau. Với hỗ trợ cho nhiều loại CAPTCHA và thời gian phản hồi nhanh như chớp, CapSolver tích hợp dễ dàng vào các quy trình tự động hóa.
Khi xây dựng tự động hóa Browser4 tương tác với các trang web được bảo vệ—dù là để trích xuất dữ liệu, theo dõi giá cả hay nghiên cứu thị trường—các thách thức CAPTCHA trở thành rào cản quan trọng. Dưới đây là lý do tại sao sự tích hợp này quan trọng:

Maven (pom.xml):
<dependencies>
<!-- Browser4/PulsarRPA -->
<dependency>
<groupId>ai.platon.pulsar</groupId>
<artifactId>pulsar-boot</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Client HTTP cho CapSolver -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- Phân tích JSON -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<!-- Coroutines Kotlin -->
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies>
Gradle (build.gradle.kts):
dependencies {
implementation("ai.platon.pulsar:pulsar-boot:2.2.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.google.code.gson:gson:2.10.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
}
Tạo một tệp application.properties:
# Cấu hình CapSolver
CAPSOLVER_API_KEY=khóa_api_capsolver_của_bạn
# Cấu hình LLM (tùy chọn, cho trích xuất AI)
OPENROUTER_API_KEY=khóa_api_openrouter_của_bạn
# Cấu hình proxy (tùy chọn)
PROXY_ROTATION_URL=địa chỉ_proxy_của_bạn
Dưới đây là một dịch vụ Kotlin có thể tái sử dụng tích hợp CapSolver với Browser4:
import com.google.gson.Gson
import com.google.gson.JsonObject
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import kotlinx.coroutines.delay
import java.util.concurrent.TimeUnit
data class TaskResult(
val gRecaptchaResponse: String? = null,
val token: String? = null,
val cookies: List<Map<String, String>>? = null,
val userAgent: String? = null
)
class CapSolverService(private val apiKey: String) {
private val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
private val gson = Gson()
private val baseUrl = "https://api.capsolver.com"
private val jsonMediaType = "application/json".toMediaType()
private suspend fun createTask(taskData: Map<String, Any>): String {
val payload = mapOf(
"clientKey" to apiKey,
"task" to taskData
)
val request = Request.Builder()
.url("$baseUrl/createTask")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
if (result.get("errorId").asInt != 0) {
throw Exception("Lỗi CapSolver: ${result.get("errorDescription").asString}")
}
return result.get("taskId").asString
}
private suspend fun getTaskResult(taskId: String, maxAttempts: Int = 60): TaskResult {
val payload = mapOf(
"clientKey" to apiKey,
"taskId" to taskId
)
repeat(maxAttempts) {
delay(2000)
val request = Request.Builder()
.url("$baseUrl/getTaskResult")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
when (result.get("status")?.asString) {
"ready" -> {
val solution = result.getAsJsonObject("solution")
return TaskResult(
gRecaptchaResponse = solution.get("gRecaptchaResponse")?.asString,
token = solution.get("token")?.asString,
userAgent = solution.get("userAgent")?.asString
)
}
"failed" -> throw Exception("Nhiệm vụ thất bại: ${result.get("errorDescription")?.asString}")
}
}
throw Exception("Hết thời gian chờ đợi giải pháp CAPTCHA")
}
suspend fun solveReCaptchaV2(websiteUrl: String, websiteKey: String): String {
val taskId = createTask(mapOf(
"type" to "ReCaptchaV2TaskProxyLess",
"websiteURL" to websiteUrl,
"websiteKey" to websiteKey
))
val result = getTaskResult(taskId)
return result.gRecaptchaResponse ?: throw Exception("Không có gRecaptchaResponse trong giải pháp")
}
suspend fun solveReCaptchaV3(
websiteUrl: String,
websiteKey: String,
pageAction: String = "submit"
): String {
val taskId = createTask(mapOf(
"type" to "ReCaptchaV3TaskProxyLess",
"websiteURL" to websiteUrl,
"websiteKey" to websiteKey,
"pageAction" to pageAction
))
val result = getTaskResult(taskId)
return result.gRecaptchaResponse ?: throw Exception("Không có gRecaptchaResponse trong giải pháp")
}
suspend fun solveTurnstile(
websiteUrl: String,
websiteKey: String,
action: String? = null,
cdata: String? = null
): String {
val taskData = mutableMapOf(
"type" to "AntiTurnstileTaskProxyLess",
"websiteURL" to websiteUrl,
"websiteKey" to websiteKey
)
// Thêm thông tin tùy chọn
if (action != null || cdata != null) {
val metadata = mutableMapOf<String, String>()
action?.let { metadata["action"] = it }
cdata?.let { metadata["cdata"] = it }
taskData["metadata"] = metadata
}
val taskId = createTask(taskData)
val result = getTaskResult(taskId)
return result.token ?: throw Exception("Không có token trong giải pháp")
}
suspend fun checkBalance(): Double {
val payload = mapOf("clientKey" to apiKey)
val request = Request.Builder()
.url("$baseUrl/getBalance")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
return result.get("balance")?.asDouble ?: 0.0
}
}
import ai.platon.pulsar.context.PulsarContexts
import ai.platon.pulsar.skeleton.session.PulsarSession
import kotlinx.coroutines.runBlocking
class ReCaptchaV2Extractor(
private val capSolver: CapSolverService
) {
suspend fun extractWithCaptcha(targetUrl: String, siteKey: String): Map<String, Any?> {
println("Giải reCAPTCHA v2...")
// Giải CAPTCHA trước tiên
val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)
println("CAPTCHA đã được giải, độ dài token: ${token.length}")
// Tạo phiên và mở trang
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Chèn token vào ô nhập văn bản ẩn bằng thuộc tính value (an toàn)
driver?.evaluate("""
(function() {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = arguments[0];
})('$token');
""")
// Gửi biểu mẫu
driver?.evaluate("document.querySelector('form').submit();")
// Chờ đợi điều hướng
Thread.sleep(3000)
// Trích xuất dữ liệu từ trang kết quả
val document = session.parse(page)
mapOf(
"title" to document.selectFirstTextOrNull("h1"),
"content" to document.selectFirstTextOrNull(".content"),
"success" to (document.body().text().contains("success", ignoreCase = true))
)
}
}
fun main() = runBlocking {
val apiKey = System.getenv("CAPSOLVER_API_KEY") ?: "khóa_api_của_bạn"
val capSolver = CapSolverService(apiKey)
val extractor = ReCaptchaV2Extractor(capSolver)
val result = extractor.extractWithCaptcha(
targetUrl = "https://example.com/trang-bi-bao-ve",
siteKey = "6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC"
)
println("Kết quả trích xuất: $result")
}
class ReCaptchaV3Extractor(
private val capSolver: CapSolverService
) {
suspend fun extractWithCaptchaV3(
targetUrl: String,
siteKey: String,
action: String = "submit"
): Map<String, Any?> {
println("Giải reCAPTCHA v3 với hành động: $action")
// Giải reCAPTCHA v3 với hành động trang tùy chỉnh
val token = capSolver.solveReCaptchaV3(
websiteUrl = targetUrl,
websiteKey = siteKey,
pageAction = action
)
println("Token đã được lấy thành công")
// Tạo phiên và mở trang
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Chèn token vào trường ẩn (sử dụng gán giá trị an toàn)
driver?.evaluate("""
(function(tokenValue) {
var input = document.querySelector('input[name="g-recaptcha-response"]');
if (input) {
input.value = tokenValue;
} else {
var hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = 'g-recaptcha-response';
hidden.value = tokenValue;
var form = document.querySelector('form');
if (form) form.appendChild(hidden);
}
})('$token');
""")
// Nhấn nút gửi
driver?.evaluate("document.querySelector('#submit-btn').click();")
Thread.sleep(3000)
val document = session.parse(page)
mapOf(
"kết quả" to document.selectFirstTextOrNull(".result-data"),
"trạng thái" to "thành công"
)
}
}
class TurnstileExtractor(
private val capSolver: CapSolverService
) {
suspend fun extractWithTurnstile(targetUrl: String, siteKey: String): Map<String, Any?> {
println("Giải Cloudflare Turnstile...")
// Giải với thông tin tùy chọn (hành động và cdata)
val taskId = capSolver.createTask(mapOf(
"type" to "AntiTurnstileTaskProxyLess",
"websiteURL" to targetUrl,
"websiteKey" to siteKey
))
val result = capSolver.getTaskResult(taskId)
return mapOf(
"token" to result.token,
"userAgent" to result.userAgent
)
}
}
val token = capSolver.solveTurnstile(
targetUrl,
siteKey,
action = "login", // tùy chọn
cdata = "0000-1111-2222-3333-example" // tùy chọn
)
println("Turnstile đã được giải!")
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Chèn token Turnstile (sử dụng gán giá trị an toàn)
driver?.evaluate("""
(function(tokenValue) {
var input = document.querySelector('input[name="cf-turnstile-response"]');
if (input) input.value = tokenValue;
})('$token');
""")
// Gửi biểu mẫu
driver?.evaluate("document.querySelector('form').submit();")
Thread.sleep(3000)
val document = session.parse(page)
mapOf(
"title" to document.selectFirstTextOrNull("title"),
"content" to document.selectFirstTextOrNull("body")?.take(500)
)
}
}
---
## Tích hợp với Browser4 X-SQL
X-SQL của Browser4 cung cấp khả năng trích xuất mạnh mẽ. Dưới đây là cách kết hợp nó với việc giải CAPTCHA:
```kotlin
class XSqlCaptchaExtractor(
private val capSolver: CapSolverService
) {
suspend fun extractProductsWithCaptcha(
targetUrl: String,
siteKey: String
): List<Map<String, Any?>> {
// Giải CAPTCHA trước
val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)
// Tạo phiên và thiết lập phiên đã xác thực
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
document.querySelector('form').submit();
})('$token');
""")
Thread.sleep(3000)
// Bây giờ phân tích trang và trích xuất dữ liệu sản phẩm
val document = session.parse(page)
// Trích xuất dữ liệu sản phẩm bằng phương thức phiên tích hợp
val products = mutableListOf<Map<String, Any?>>()
val productElements = document.select(".product-item")
for ((index, element) in productElements.withIndex()) {
if (index >= 50) break // GIỚI HẠN 50
products.add(mapOf(
"name" to element.selectFirstTextOrNull(".product-name"),
"price" to element.selectFirstTextOrNull(".price")?.let {
"""(\d+\.?\d*)""".toRegex().find(it)?.groupValues?.get(1)?.toDoubleOrNull() ?: 0.0
},
"rating" to element.selectFirstTextOrNull(".rating")
))
}
return products.map { row ->
mapOf(
"name" to row["name"],
"price" to row["price"],
"rating" to row["rating"],
"image_url" to row["image_url"]
)
}
}
}
Đối với các trang yêu cầu CAPTCHA trước khi truy cập nội dung, sử dụng quy trình xác thực trước:
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl
class PreAuthenticator(
private val capSolver: CapSolverService
) {
data class AuthSession(
val cookies: Map<String, String>,
val userAgent: String?
)
suspend fun authenticateWithCaptcha(
loginUrl: String,
siteKey: String
): AuthSession {
// Giải CAPTCHA
val captchaToken = capSolver.solveReCaptchaV2(loginUrl, siteKey)
// Gửi CAPTCHA để lấy cookie phiên
val client = OkHttpClient.Builder()
.cookieJar(object : CookieJar {
private val cookies = mutableListOf<Cookie>()
override fun saveFromResponse(url: HttpUrl, cookieList: List<Cookie>) {
cookies.addAll(cookieList)
}
override fun loadForRequest(url: HttpUrl): List<Cookie> = cookies
})
.build()
val formBody = okhttp3.FormBody.Builder()
.add("g-recaptcha-response", captchaToken)
.build()
val request = Request.Builder()
.url(loginUrl)
.post(formBody)
.build()
val response = client.newCall(request).execute()
// Trích xuất cookie từ phản hồi
val responseCookies = response.headers("Set-Cookie")
.associate { cookie ->
val parts = cookie.split(";")[0].split("=", limit = 2)
parts[0] to (parts.getOrNull(1) ?: "")
}
return AuthSession(
cookies = responseCookies,
userAgent = response.request.header("User-Agent")
)
}
}
class AuthenticatedExtractor(
private val preAuth: PreAuthenticator,
private val capSolver: CapSolverService
) {
suspend fun extractWithAuth(
loginUrl: String,
targetUrl: String,
siteKey: String
): Map<String, Any?> {
// Xác thực trước
val authSession = preAuth.authenticateWithCaptcha(loginUrl, siteKey)
println("Phiên đã được thiết lập với ${authSession.cookies.size} cookie")
// Tạo phiên Browser4
val session = PulsarContexts.createSession()
// Cấu hình phiên với cookie
val cookieScript = authSession.cookies.entries.joinToString(";") { (k, v) ->
"$k=$v"
}
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
// Thiết lập cookie
driver?.evaluate("document.cookie = '$cookieScript';")
// Tải lại với phiên đã xác thực
driver?.evaluate("location.reload();")
Thread.sleep(2000)
// Trích xuất dữ liệu
val document = session.parse(page)
return mapOf(
"authenticated" to true,
"content" to document.selectFirstTextOrNull(".protected-content"),
"userData" to document.selectFirstTextOrNull(".user-profile")
)
}
}
Các khả năng AI của Browser4 có thể được nâng cao bằng OpenRouter, một cổng API thống nhất để truy cập nhiều mô hình LLM. Điều này cho phép trích xuất nội dung thông minh, thích ứng với các cấu trúc trang khác nhau.
import com.google.gson.Gson
import com.google.gson.JsonObject
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit
data class ChatMessage(val role: String, val content: String)
data class ChatCompletion(val content: String, val model: String, val usage: TokenUsage)
data class TokenUsage(val promptTokens: Int, val completionTokens: Int, val totalTokens: Int)
class OpenRouterService(private val apiKey: String) {
private val client = OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build()
private val gson = Gson()
private val baseUrl = "https://openrouter.ai/api/v1"
private val jsonMediaType = "application/json".toMediaType()
fun chat(
messages: List<ChatMessage>,
model: String = "openai/gpt-4o-mini"
): ChatCompletion {
val payload = mapOf(
"model" to model,
"messages" to messages.map { mapOf("role" to it.role, "content" to it.content) }
)
val request = Request.Builder()
.url("$baseUrl/chat/completions")
.header("Authorization", "Bearer $apiKey")
.post(gson.toJson(payload).toRequestBody(jsonMediaType))
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
val choice = result.getAsJsonArray("choices")?.get(0)?.asJsonObject
val content = choice?.getAsJsonObject("message")?.get("content")?.asString ?: ""
val usage = result.getAsJsonObject("usage")
return ChatCompletion(
content = content,
model = result.get("model")?.asString ?: model,
usage = TokenUsage(
promptTokens = usage?.get("prompt_tokens")?.asInt ?: 0,
completionTokens = usage?.get("completion_tokens")?.asInt ?: 0,
totalTokens = usage?.get("total_tokens")?.asInt ?: 0
)
)
}
fun extractStructuredData(html: String, schema: String): String {
val prompt = """
Trích xuất dữ liệu sau từ nội dung HTML này.
Trả về CHỈ JSON hợp lệ khớp với lược đồ này: $schema
HTML:
${html.take(4000)}
""".trimIndent()
return chat(listOf(ChatMessage("user", prompt))).content
}
fun listModels(): List<String> {
val request = Request.Builder()
.url("$baseUrl/models")
.header("Authorization", "Bearer $apiKey")
.build()
val response = client.newCall(request).execute()
val result = gson.fromJson(response.body?.string(), JsonObject::class.java)
return result.getAsJsonArray("data")?.mapNotNull {
it.asJsonObject.get("id")?.asString
} ?: emptyList()
}
}
Kết hợp giải CAPTCHA với trích xuất dữ liệu thông minh:
class SmartExtractor(
private val capSolver: CapSolverService,
private val openRouter: OpenRouterService
) {
suspend fun extractWithAI(
targetUrl: String,
siteKey: String?,
extractionPrompt: String
): Map<String, Any?> {
// Bước 1: Giải CAPTCHA nếu cần
val captchaToken = siteKey?.let {
println("Giải CAPTCHA...")
capSolver.solveReCaptchaV2(targetUrl, it)
}
// Bước 2: Tạo phiên và mở trang
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
captchaToken?.let { token ->
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
var form = document.querySelector('form');
if (form) form.submit();
})('$token');
""")
Thread.sleep(3000)
}
// Bước 3: Trích xuất nội dung trang
val document = session.parse(page)
val pageContent = document.body().text().take(8000)
// Bước 4: Sử dụng LLM để trích xuất dữ liệu có cấu trúc
val llmResponse = openRouter.chat(listOf(
ChatMessage("system", "Bạn là một trợ lý trích xuất dữ liệu. Trích xuất dữ liệu có cấu trúc từ các trang web."),
ChatMessage("user", """
$extractionPrompt
Nội dung trang:
$pageContent
""".trimIndent())
))
println("LLM đã sử dụng ${llmResponse.usage.totalTokens} token")
return mapOf(
"url" to targetUrl,
"captchaSolved" to (captchaToken != null),
"extractedData" to llmResponse.content,
"tokensUsed" to llmResponse.usage.totalTokens
)
}
}
// Cách sử dụng
fun main() = runBlocking {
val capSolver = CapSolverService(System.getenv("CAPSOLVER_API_KEY")!!)
val openRouter = OpenRouterService(System.getenv("OPENROUTER_API_KEY")!!)
val extractor = SmartExtractor(capSolver, openRouter)
val result = extractor.extractWithAI(
targetUrl = "https://example.com/products",
siteKey = "6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC",
extractionPrompt = """
Trích xuất tất cả sản phẩm với:
- tên
- giá (dưới dạng số)
- tình trạng tồn kho (trong kho/đã hết)
- đánh giá (1-5)
Trả về dưới dạng mảng JSON.
""".trimIndent()
)
println("Kết quả trích xuất: ${result["extractedData"]}")
}
Sử dụng LLM để tạo các bộ chọn CSS cho các cấu trúc trang chưa biết:
class AdaptiveExtractor(
private val capSolver: CapSolverService,
private val openRouter: OpenRouterService
) {
suspend fun extractWithAdaptiveSelectors(
targetUrl: String,
siteKey: String?,
dataFields: List<String>
): Map<String, Any?> {
// Giải CAPTCHA trước tiên
val token = siteKey?.let { capSolver.solveReCaptchaV2(targetUrl, it) }
val session = PulsarContexts.createSession()
val page = session.open(targetUrl)
val driver = session.getOrCreateBoundDriver()
token?.let { t ->
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
})('$t');
""")
}
// Lấy cấu trúc HTML của trang
val htmlSample = driver?.evaluate("document.body.innerHTML")?.toString()?.take(5000) ?: ""
// Yêu cầu LLM tạo các bộ chọn
val selectorPrompt = """
Phân tích HTML này và cung cấp các bộ chọn CSS cho các trường sau: ${dataFields.joinToString(", ")}
Mẫu HTML:
$htmlSample
Trả về JSON như: {"fieldName": "css-selector", ...}
""".trimIndent()
val selectorsJson = openRouter.chat(listOf(ChatMessage("user", selectorPrompt))).content
val selectors = Gson().fromJson(selectorsJson, Map::class.java) as Map<String, String>
// Trích xuất bằng các bộ chọn được tạo
val document = session.parse(page)
val extractedData = selectors.mapValues { (_, selector) ->
document.selectFirstTextOrNull(selector)
}
return mapOf(
"url" to targetUrl,
"selectors" to selectors,
"data" to extractedData
)
}
}
Thiết kế an toàn coroutine của Browser4 cho phép xử lý CAPTCHA hiệu quả theo cách song song:
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
data class ExtractionJob(
val url: String,
val siteKey: String?
)
data class ExtractionResult(
val url: String,
val data: Map<String, Any?>?,
val captchaSolved: Boolean,
val error: String?,
val duration: Long
)
class ParallelExtractor(
private val capSolver: CapSolverService,
private val concurrency: Int = 5
) {
suspend fun extractAll(jobs: List<ExtractionJob>): List<ExtractionResult> = coroutineScope {
val channel = Channel<ExtractionJob>(Channel.UNLIMITED)
val results = mutableListOf<ExtractionResult>()
// Gửi tất cả công việc đến kênh
jobs.forEach { channel.send(it) }
channel.close()
// Xử lý với độ đồng thời giới hạn
val workers = (1..concurrency).map { workerId ->
async {
val workerResults = mutableListOf<ExtractionResult>()
// Mỗi công nhân tạo phiên riêng để đảm bảo an toàn luồng
val workerSession = PulsarContexts.createSession()
for (job in channel) {
val startTime = System.currentTimeMillis()
var captchaSolved = false
try {
// Giải CAPTCHA nếu có site key
val token = job.siteKey?.let {
captchaSolved = true
capSolver.solveReCaptchaV2(job.url, it)
}
// Trích xuất dữ liệu
val page = workerSession.open(job.url)
token?.let { t ->
val driver = workerSession.getOrCreateBoundDriver()
driver?.evaluate("""
(function(tokenValue) {
var el = document.querySelector('#g-recaptcha-response');
if (el) el.value = tokenValue;
})('$t');
""")
}
val document = workerSession.parse(page)
workerResults.add(ExtractionResult(
url = job.url,
data = mapOf(
"title" to document.selectFirstTextOrNull("title"),
"h1" to document.selectFirstTextOrNull("h1")
),
captchaSolved = captchaSolved,
error = null,
duration = System.currentTimeMillis() - startTime
))
} catch (e: Exception) {
workerResults.add(ExtractionResult(
url = job.url,
data = null,
captchaSolved = captchaSolved,
error = e.message,
duration = System.currentTimeMillis() - startTime
))
}
}
workerResults
}
}
workers.awaitAll().flatten()
}
}
// Cách sử dụng
fun main() = runBlocking {
val capSolver = CapSolverService(System.getenv("CAPSOLVER_API_KEY")!!)
val extractor = ParallelExtractor(capSolver, concurrency = 5)
val jobs = listOf(
ExtractionJob("https://site1.com/data", "6Lc..."),
ExtractionJob("https://site2.com/data", null),
ExtractionJob("https://site3.com/data", "6Lc..."),
)
val results = extractor.extractAll(jobs)
val solved = results.count { it.captchaSolved }
println("Hoàn thành ${results.size} trích xuất, giải được $solved CAPTCHA")
results.forEach { r ->
println("${r.url}: ${r.duration}ms - ${r.error ?: "thành công"}")
}
}
suspend fun <T> withRetry(
maxRetries: Int = 3,
initialDelay: Long = 1000,
block: suspend () -> T
): T {
var lastException: Exception? = null
repeat(maxRetries) { attempt ->
try {
return block()
} catch (e: Exception) {
lastException = e
println("Lần thử ${attempt + 1} thất bại: ${e.message}")
delay(initialDelay * (attempt + 1))
}
}
throw lastException ?: Exception("Đạt giới hạn thử lại")
}
// Cách sử dụng
val token = withRetry(maxRetries = 3) {
capSolver.solveReCaptchaV2(url, siteKey)
}
suspend fun ensureSufficientBalance(
capSolver: CapSolverService,
minBalance: Double = 1.0
) {
val balance = capSolver.checkBalance()
if (balance < minBalance) {
throw Exception("Số dư CapSolver không đủ: $${"%.2f".format(balance)}. Vui lòng nạp thêm.")
}
println("Số dư CapSolver: $${"%.2f".format(balance)}")
}
class TokenCache(private val ttlMs: Long = 90_000) {
private data class CachedToken(val token: String, val timestamp: Long)
private val cache = mutableMapOf<String, CachedToken>()
private fun getKey(domain: String, siteKey: String) = "$domain:$siteKey"
fun get(domain: String, siteKey: String): String? {
val key = getKey(domain, siteKey)
val cached = cache[key] ?: return null
if (System.currentTimeMillis() - cached.timestamp > ttlMs) {
cache.remove(key)
return null
}
return cached.token
}
fun set(domain: String, siteKey: String, token: String) {
val key = getKey(domain, siteKey)
cache[key] = CachedToken(token, System.currentTimeMillis())
}
}
// Cách sử dụng với bộ đệm
class CachedCapSolver(
private val capSolver: CapSolverService,
private val cache: TokenCache = TokenCache()
) {
suspend fun solveReCaptchaV2Cached(websiteUrl: String, websiteKey: String): String {
val domain = java.net.URL(websiteUrl).host
cache.get(domain, websiteKey)?.let {
println("Sử dụng token đã lưu")
return it
}
val token = capSolver.solveReCaptchaV2(websiteUrl, websiteKey)
cache.set(domain, websiteKey, token)
return token
}
}
| Thiết lập | Mô tả | Mặc định |
|---|---|---|
CAPSOLVER_API_KEY |
Mã API của bạn | - |
OPENROUTER_API_KEY |
Mã API OpenRouter cho tính năng LLM | - |
PROXY_ROTATION_URL |
URL dịch vụ thay đổi proxy | - |
Browser4 sử dụng application.properties cho cấu hình bổ sung |
Việc tích hợp CapSolver với Browser4 tạo ra một sự kết hợp mạnh mẽ cho trích xuất dữ liệu web quy mô lớn. Kiến trúc an toàn coroutine và khả năng hiệu suất cực cao của Browser4, kết hợp với khả năng giải CAPTCHA đáng tin cậy của CapSolver, cho phép trích xuất dữ liệu ở quy mô lớn.
Các mẫu tích hợp chính:
Dù bạn đang xây dựng hệ thống theo dõi giá cả, quy trình nghiên cứu thị trường hay nền tảng tổng hợp dữ liệu, sự kết hợp Browser4 + CapSolver cung cấp độ tin cậy và khả năng mở rộng cần thiết cho môi trường sản xuất.
Sẵn sàng bắt đầu chưa? Đăng ký CapSolver và sử dụng mã khuyến mãi BROWSER4 để nhận thêm 6% tiền thưởng cho lần nạp đầu tiên!
Browser4 là khung phần mềm tự động hóa trình duyệt hiệu suất cao, an toàn với coroutine từ PulsarRPA. Được xây dựng bằng Kotlin, nó được thiết kế cho trích xuất dữ liệu được hỗ trợ AI, hỗ trợ 100.000-200.000 lần truy cập trang phức tạp mỗi máy mỗi ngày.
CapSolver tích hợp với Browser4 thông qua lớp dịch vụ giải CAPTCHA qua API CapSolver. Các token đã giải được chèn vào trang bằng khả năng đánh giá JavaScript của Browser4 (driver.evaluate()).
CapSolver hỗ trợ reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile, Cloudflare Challenge (5s), AWS WAF, GeeTest v3/v4 và nhiều loại khác.
CapSolver cung cấp giá cả cạnh tranh dựa trên loại và khối lượng CAPTCHA được giải. Truy cập capsolver.com để xem giá hiện tại. Sử dụng mã BROWSER4 để nhận 6% tiền thưởng.
Browser4 được xây dựng bằng Kotlin và chạy trên JVM (Java 17+). Nó cũng có thể được sử dụng từ các ứng dụng Java.
Có! Thiết kế an toàn coroutine của Browser4 cho phép xử lý hiệu quả song song. Kết hợp với API CapSolver, bạn có thể giải nhiều CAPTCHA đồng thời trên các công việc trích xuất khác nhau.
Khóa site thường được tìm thấy trong mã nguồn HTML của trang:
data-sitekey trên phần tử .g-recaptchadata-sitekey trên phần tử .cf-turnstileHọc kiến trúc gỡ mã web Rust có thể mở rộng với reqwest, scraper, gỡ mã bất đồng bộ, gỡ mã trình duyệt không đầu, xoay proxy và xử lý CAPTCHA tuân thủ.

Tự động hóa việc giải CAPTCHA với Nanobot và CapSolver. Sử dụng Playwright để giải reCAPTCHA và Cloudflare tự động.
